home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Developer Essentials / DTS Sample Code / Macintosh Sample Code / SC.015.Offscreen / Offscreen.inc1.p next >
Encoding:
Text File  |  1989-03-31  |  26.8 KB  |  876 lines  |  [TEXT/MPS ]

  1. {------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    "Skippy White's Famous High Level Off-Screen Map Routines”
  6. #
  7. #    Offscreen.inc1.p    -    Pascal Source
  8. #
  9. #    Copyright © 1989 Apple Computer, Inc.
  10. #    All rights reserved.
  11. #    The characters depicted herein (except Skippy White) are fictitious.
  12. #
  13. #    Versions:    
  14. #                1.00                04/89
  15. #
  16. #    Components:    
  17. #                Offscreen.p            April 1, 1988
  18. #                Offscreen.inc1.p    April 1, 1988
  19. #
  20. #    These routines provide a high-level interface to the QuickDraw & Color
  21. #    Manager routines which allow the creation and manipulation of off-screen
  22. #    bitmaps and pixmaps. They are designed to run on any machine with 128K or
  23. #    later ROMs (sorry 64K ROM fans).
  24. #    Note that the design incorporates the idea that you can go along pretending
  25. #    there is an offscreen buffer even when one couldn’t be allocated, and the
  26. #    calls will do nothing.
  27. #
  28. ------------------------------------------------------------------------------}
  29.  
  30. CONST
  31.     noDepth = -2;
  32.     chunky    = 0;
  33.  
  34. TYPE
  35.     PrivateHandle    = ^PrivatePtr;
  36.     PrivatePtr        = ^PrivateRecord;
  37.     PrivateRecord    = RECORD
  38.         requestedBounds    : Rect; {boundary rectangle for the pixmap}
  39.         {used to determine the depth, size, and dimensions of the pixmap}
  40.  
  41.         requestedDepth    : INTEGER; {the requested depth of this map}
  42.         {if > 0, a private device will be created for the map}
  43.  
  44.         requestedColors    : CTabHandle; {color table}
  45.         {our own copy of the color table that was passed in}
  46.  
  47.         requestedPolite    : BOOLEAN; {whether he wanted it polite}
  48.  
  49.         bits            : Handle; {handle to the off-screen bits}
  50.         {if NIL, there is no port or graphics device either}
  51.         notOurs            : BOOLEAN; {TRUE means that we didn’t create the bits}
  52.         needs32Bits        : BOOLEAN; {you must be in 32 bit memory mode to get to bits}
  53.  
  54.         bitsPort        : CGrafPtr; {GrafPort used for the off-screen map.}
  55.         bitsDevice        : GDHandle; {GDevice associated with the map}
  56.         {private device (if requestedDepth > 0)}
  57.         {(from the device list for requestedDepth = kMaxDepth)}
  58.  
  59.         oldSeed            : LONGINT; {old CTSeed for the pixmap's color table}
  60.         {used to detect color updates}
  61.         
  62.         invalRegion        : RgnHandle; {part of offscreen buffer that is invalid}
  63.  
  64.         drawingPort        : GrafPtr; {supplied to BeginOffscreenDahling}
  65.         savedPort        : GrafPtr; {saves old GrafPort while drawing off screen}
  66.         savedMap        : BitMap; {used to save map when we don’t use our port}
  67.         savedVisRgn        : RgnHandle; {saves the visRgn from a user-supplied port}
  68.         savedDevice        : GDHandle; {saves old GDevice while drawing off screen}
  69.     END; {PrivateRecord}
  70.  
  71.  
  72.  
  73. VAR
  74.     theMac    : SysEnvRec;
  75.  
  76.  
  77. {---------- low-level service routines used by the rest of Offscreen ----------}
  78.  
  79. {Fill out the pixmap according to the bounds, pixelType, pixelSize, cmpCount,
  80.  cmpSize and pmTable parameters. All fields will be modified.
  81.  The initial pixmap will be copied from the current GDevice’s pixmap, so you must modify
  82.  the pmap^^.baseAddr before using it.
  83.  dataSize will return with the number of bytes required for the pixel data.
  84.  Note: it is the responsibility of the caller to fill in the baseAddr field
  85.  of the pixmap. You may want to call NewImprovedGBuffer to see if the system will
  86.  allocate the data space and then set baseAddr to point to it.
  87.  This call cannot fail.}
  88. PROCEDURE InitGBufferPixmap(pmap: PixMapHandle; aBounds: Rect; aPixelType, aPixelSize,
  89.      aCmpCount, aCmpSize: INTEGER; aPMTable: CTabHandle; VAR dataSize: LONGINT);
  90.     
  91. VAR
  92.     theGDevice:    GDHandle;
  93. BEGIN
  94.     theGDevice := GetGDevice;                {get the current device}
  95.     pmap^^:=theGDevice^^.gdPMap^^;            {start out with device’s pixmap}
  96.     WITH pmap^^ DO BEGIN
  97.         bounds := aBounds;
  98.         pixelType := aPixelType;
  99.         pixelSize := aPixelSize;
  100.          cmpCount := aCmpCount;
  101.         cmpSize := aCmpSize;
  102.         pmTable := aPMTable;
  103.         WITH bounds DO BEGIN
  104.             rowBytes := ((pixelSize * right + 15) DIV 16) * 2;
  105.             {calculate an even # of words}
  106.             dataSize := bottom * LONGINT(rowBytes); {calculate the size}
  107.             rowBytes := rowBytes + $8000;    {flag that it’s a pixmap}
  108.         END; {WITH}
  109.     END; {WITH}
  110. END; {InitGBufferPixmap}
  111.     
  112. {Fill out a pixmap for use with an onscreen grafPort. It differs from InitGBufferPixmap
  113.  in that it associates the buffer with an onscreen device. This will be the maximim-
  114.  depth device which intersects the globalBounds rectangle. In the most common case
  115.  you will pass in the portRect of a window after converting it to global coordinates.
  116.  If no screen device intersects globalBounds, the function will return FALSE and
  117.  device will return with NIL. If it returns TRUE then device will be a handle to
  118.  the proper GDevice (see StartLeech, below).
  119.  For the other information, see the comments at InitGBufferPixmap.}
  120. FUNCTION InitGBufferScreen(pmap: PixMapHandle; globalBounds: Rect; VAR device:GDHandle;
  121.       VAR dataSize: LONGINT): BOOLEAN;
  122.  
  123. VAR
  124.     savedDevice:    GDHandle;
  125. BEGIN
  126.     InitGBufferScreen := FALSE;
  127.     
  128.     device := GetMaxDevice(globalBounds);        {maximum-depth device for globalBounds}
  129.     IF device <> NIL THEN BEGIN
  130.         savedDevice := GetGDevice;
  131.         
  132.         WITH globalBounds DO
  133.             SetRect(globalBounds, 0, 0, right - left, bottom - top);
  134.         {make globalBounds into a window-ish (zero-based) rectangle}
  135.             
  136.         SetGDevice(device);                        {set to device for InitGBufferPixmap}
  137.         WITH device^^.gdPMap^^ DO
  138.             InitGBufferPixmap(pmap, globalBounds, pixelType, pixelSize,
  139.                  cmpCount, cmpSize, pmTable, dataSize);
  140.             {set up the pixmap}
  141.             
  142.         SetGDevice(savedDevice);
  143.         InitGBufferScreen := TRUE;
  144.     END; {IF device <> NIL}
  145. END; {InitGBufferScreen}
  146.  
  147. {Allocate the data space for the bits of an offscreen buffer. Only do so if
  148.  it can be allocated in an optimum place for graphics use. If the system
  149.  doesn’t have a superior place to allocate the bits, NIL is returned.
  150.  needs32Bits will return TRUE if the bit space requires 32 bit addressing
  151.  in order to access it. buffNotNeeded will return TRUE if performance will be
  152.  improved by NOT buffering (“performance” means avoiding flicker, etc.)}
  153. FUNCTION NewImprovedGBuffer(dataSize: LONGINT; VAR needs32Bits: BOOLEAN;
  154.             VAR buffNotNeeded: BOOLEAN): Ptr;
  155.  
  156. BEGIN
  157.     NewImprovedGBuffer := NIL;
  158.     needs32Bits := FALSE;
  159.     buffNotNeeded := FALSE;
  160. END; {NewImprovedGBuffer}
  161.  
  162. {Free a buffer allocated via NewImprovedGBuffer.}
  163. PROCEDURE FreeEnhancedGBuffer(bits: Ptr);
  164.  
  165. BEGIN
  166. END; {FreeEnhancedGBuffer}
  167.  
  168. {Fill out the GDevice record and set device^^.gdPMap = pixmap. Errors can occur from not
  169.  being able to build the ITable, etc.}
  170. FUNCTION InitGBufferDevice(device: GDHandle; pmap: PixMapHandle): OSErr;
  171.  
  172. VAR
  173.     preferredResolution:    INTEGER;
  174.     inverseTable:             ITabHandle;
  175.  
  176.     PROCEDURE Out(error: OSErr);
  177.     BEGIN {Out}
  178.         IF error <> noErr THEN BEGIN
  179.             DisposHandle(Handle(device^^.gdITable));    {get rid of the inverse table}
  180.             InitGBufferDevice := error;
  181.             EXIT(InitGBufferDevice);
  182.         END; {IF error <> noErr}
  183.     END; {Out}
  184.  
  185. BEGIN
  186.     device^^.gdITable := NIL;
  187.     inverseTable := ITabHandle(NewHandle(0));    {create the inverse table stub}
  188.     Out(MemError);                                {bail if couldn’t make the handle}
  189.     MakeITable(pmap^^.pmTable, inverseTable, preferredResolution);
  190.     Out(QDError);                                {bail if MakeITable failed}
  191.     
  192.     preferredResolution := GetMainDevice^^.gdResPref; {preferred resolution ??? is this cool?}
  193.     
  194.     WITH device^^ DO BEGIN
  195.         gdType := clutType;                        {no private devices for direct ones, right?}    
  196.         gdITable := inverseTable;
  197.         gdResPref := preferredResolution;
  198.         gdFlags := 2 ** noDriver;
  199.         gdPMap := pmap;
  200.         gdRect := pmap^^.bounds;
  201.     END; {WITH}
  202.     
  203.     InitGBufferDevice := noErr;
  204. END; {InitGBufferDevice}
  205.  
  206.  
  207. {---------- end of low-level service routines ----------}
  208.  
  209.  
  210. PROCEDURE InitOffscreen;
  211. {128K ROMs and new enough Palette Manager}
  212.  
  213. VAR
  214.     error    : OSErr;
  215.  
  216. BEGIN {InitOffscreen}
  217.     error := SysEnvirons(1, theMac);
  218. END; {InitOffscreen}
  219.  
  220.  
  221. (* ??? commented out since we no longer use it (but someone may want it, so should we provide it as a utility?)
  222. FUNCTION GetMaxAreaDevice(globalRect: Rect): GDHandle;
  223. {Find the greatest overlap device for the given global rectangle.}
  224.  
  225. VAR
  226.     area            : LONGINT;
  227.     maxArea            : LONGINT;
  228.     device            : GDHandle;
  229.     intersection    : Rect;
  230.  
  231. BEGIN {GetMaxAreaDevice}
  232.     GetMaxAreaDevice := NIL;
  233.  
  234.     maxArea := 0;
  235.  
  236.     device := GetDeviceList;
  237.     WHILE device <> NIL DO BEGIN
  238.         IF TestDeviceAttribute(device, screenDevice) THEN
  239.             IF TestDeviceAttribute(device, screenActive) THEN
  240.                 IF SectRect(globalRect, device^^.gdRect, intersection) THEN BEGIN
  241.                     WITH intersection DO
  242.                         area := LONGINT(right - left) * LONGINT(bottom - top);
  243.                     IF area > maxArea THEN BEGIN
  244.                         GetMaxAreaDevice := device;
  245.                         maxArea := area;
  246.                     END; {IF area > maxArea}
  247.                 END; {IF SectRect...}
  248.         device := GetNextDevice(device);
  249.     END; {WHILE device <> NIL}
  250. END; {GetMaxAreaDevice}
  251. *)
  252.  
  253.  
  254.  
  255. FUNCTION MakeCleanColorTable(colors: CTabHandle; depth: INTEGER;
  256.                              VAR table: CTabHandle): OSErr;
  257. {This call takes a handle to a list of colors to be included in a color table.
  258.  It returns a table suitable for use for a PixMap, with black and white at the
  259.  correct offsets. If there is no default color table for the depth in question,
  260.  it returns NIL for the table.}
  261.  
  262.     FUNCTION IsWhite(rgb: RGBColor): BOOLEAN;
  263.     BEGIN {IsWhite}
  264.         IsWhite := (rgb.red = $FFFF) & (rgb.green = $FFFF) & (rgb.blue = $FFFF);
  265.     END; {IsWhite}
  266.  
  267.     FUNCTION IsBlack(rgb: RGBColor): BOOLEAN;
  268.     BEGIN {IsBlack}
  269.         IsBlack := (rgb.red = 0) & (rgb.green = 0) & (rgb.blue = 0);
  270.     END; {IsBlack}
  271.  
  272. VAR
  273.     tableIndex    : INTEGER;
  274.     colorIndex    : INTEGER;
  275.     aColor        : RGBColor;
  276.  
  277. BEGIN {MakeCleanColorTable}
  278.     table := GetCTable(depth);
  279.     IF table = NIL THEN
  280.         MakeCleanColorTable := memFullErr
  281.     ELSE
  282.         WITH table^^ DO BEGIN
  283.             ctSeed := GetCTSeed; {get a fresh seed; we'll change the color table}
  284.  
  285.             tableIndex := 1; {skip white in table we are making}
  286.             colorIndex := 0; {start with first color}
  287.  
  288.             REPEAT
  289.                 IF colorIndex > colors^^.ctSize THEN
  290.                     Leave; {done with all the colors}
  291.                 aColor := colors^^.ctTable[colorIndex].rgb;
  292.                 colorIndex := colorIndex + 1; {advance to next color}
  293.                 IF IsWhite(aColor) THEN
  294.                     Cycle; {this one is white, try another}
  295.                 IF IsBlack(aColor) THEN
  296.                     Cycle; {this one is black, try another}
  297.  
  298.         {$PUSH} {$R-}
  299.                 ctTable[tableIndex].rgb := aColor; {use this color}
  300.         {$POP}
  301.                 tableIndex := tableIndex + 1;
  302.             UNTIL tableIndex > ctSize - 1; {until the table is full}
  303.  
  304.             MakeCleanColorTable := noErr;
  305.         END; {WITH}
  306. END; {MakeCleanColorTable}
  307.  
  308.  
  309.  
  310. PROCEDURE DeallocateBits(offscreenHandle: Handle);
  311. {Get rid of the bits from an off-screen map.}
  312.  
  313. BEGIN {DeallocateBits}
  314.     IF offscreenHandle <> NIL THEN
  315.         WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  316.             IF NOT notOurs THEN
  317.                 DisposHandle(bits) {get rid of the bits}
  318.             ELSE
  319.                 FreeEnhancedGBuffer(Ptr(bits));
  320.             bits := NIL;
  321.  
  322.             IF bitsPort <> NIL THEN BEGIN
  323.                 IF bitsPort^.visRgn <> NIL THEN BEGIN {visRgn of NIL means port is not open yet}
  324.                     IF requestedDepth > 0 THEN {if we created a private device and color table}
  325.                         DisposHandle(Handle(bitsPort^.portPixMap^^.pmTable)); {get rid of the colors}
  326.                     ClosePort(GrafPtr(bitsPort));
  327.             
  328.                 DisposeRgn(invalRegion); {get rid of the invalidation region}
  329.                 END; {IF ...bitsPort^.visRgn <> NIL}
  330.                 DisposPtr(Ptr(bitsPort));
  331.             END; {IF bitsPort <> NIL}
  332.             bitsPort := NIL;
  333.  
  334.             IF bitsDevice <> NIL THEN
  335.                 IF requestedDepth > 0 THEN BEGIN {if we created a private device}
  336.                     bitsDevice^^.gdPMap := NIL; {the pixmap was owned by the port}
  337.                     DisposGDevice(bitsDevice);
  338.                     bitsDevice := NIL;
  339.                 END; {IF requestedDepth > 0}
  340.         END; {WITH}
  341. END; {DeallocateBits}
  342.  
  343.  
  344.  
  345. FUNCTION GetMap(offscreenHandle: Handle): BitMapPtr;
  346. {Get a BitMapPtr for the bits in the offscreen handle.
  347.  This is also the best way to see if the bits are allocated.
  348.  Note that this has a side-effect of setting up the baseAddr properly.}
  349.  
  350. VAR
  351.     map    : BitMapPtr;
  352.  
  353. BEGIN {GetMap}
  354.     map := NIL;
  355.  
  356.     IF offscreenHandle <> NIL THEN {if we have bits}
  357.         WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  358.             IF bits <> NIL THEN {if we seem to have bits, make sure that we really do}
  359.                 IF StripAddress(bits^) = NIL THEN
  360.                     DeallocateBits(offscreenHandle)
  361.                 ELSE BEGIN
  362.                     {find the map}
  363.                     WITH bitsPort^ DO
  364.                         IF portVersion > 0 THEN {old-style port?}
  365.                             map := @portPixMap {this is really portBits!}
  366.                         ELSE
  367.                             map := BitMapPtr(portPixMap^);
  368.                     IF notOurs THEN
  369.                         map^.baseAddr := Ptr(bits)
  370.                     ELSE
  371.                         map^.baseAddr := bits^;
  372.                 END; {ELSE}
  373.         END; {WITH}
  374.     GetMap := map;
  375. END; {GetMap}
  376.  
  377.  
  378.  
  379. FUNCTION GetBitsHandle(offscreenHandle: Handle): Handle;
  380. BEGIN
  381.     GetBitsHandle := NIL;
  382.     IF offscreenHandle <> NIL THEN
  383.         WITH PrivateHandle(offscreenHandle)^^ DO
  384.             IF NOT notOurs THEN
  385.                 GetBitsHandle := bits;
  386. END; {GetBitsHandle}
  387.  
  388.  
  389.  
  390. FUNCTION AllocateBits(offscreenHandle: Handle; VAR buffNotNeeded: BOOLEAN): OSErr;
  391. {Allocate the bits, port, device, etc. for the off-screen map.
  392.  If an error code is returned, the handle will be in the "dormant" stage. ??? define
  393.  If the bits are already allocated, then nothing will happen.}
  394.  
  395. VAR
  396.     oldPort                : GrafPtr;
  397.     bounder                : Rect;
  398.     map                    : BitMapPtr;
  399.     bitsSize            : LONGINT;
  400.     colors                : CTabHandle;
  401.     preferredResolution    : INTEGER;
  402.     inverseTable        : ITabHandle;
  403.     totalSpace            : LONGINT;
  404.     contiguousSpace        : LONGINT;
  405.     use32Bits            : BOOLEAN;
  406.  
  407.     PROCEDURE Out(error: OSErr);
  408.     BEGIN {Out}
  409.         IF error <> noErr THEN BEGIN
  410.             HUnlock(offscreenHandle);
  411.             DeallocateBits(offscreenHandle);
  412.             SetPort(oldPort);
  413.             AllocateBits := error;
  414.             EXIT(AllocateBits);
  415.         END; {IF error <> noErr}
  416.     END; {Out}
  417.  
  418. BEGIN {AllocateBits}
  419.     IF offscreenHandle <> NIL THEN
  420.         IF GetMap(offscreenHandle) = NIL THEN BEGIN {if the bits are not already allocated}
  421.             GetPort(oldPort);
  422.  
  423.             MoveHHi(offscreenHandle); {fly the handle high in the heap since we lock it down}
  424.             HLock(offscreenHandle);
  425.             WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  426.                 bitsPort := CGrafPtr(NewPtrClear(SIZEOF(GrafPort))); {create a new port}
  427.                 {NewPtrClear means that bits := NIL, invalRegion := NIL, notOurs := FALSE, etc.}
  428.                 Out(MemError);
  429.  
  430.                 WITH requestedBounds DO
  431.                     SetRect(bounder, 0, 0, right - left, bottom - top);
  432.  
  433.                 IF theMac.hasColorQD THEN
  434.                     WITH bitsPort^ DO BEGIN
  435.                         OpenCPort(bitsPort); {always use a color port}
  436.                         portPixMap^^.pmTable := NIL; {in case we fail before we make a color table}
  437.                         
  438.                         IF requestedDepth > 0 THEN BEGIN {if there is a private device}
  439.                             {make a color table from the requested colors or a resource file or ROM}
  440.                             Out(MakeCleanColorTable(requestedColors, requestedDepth, colors));
  441.                             
  442.                             {now set up the pixmap of the port}
  443.                             InitGBufferPixmap(portPixMap, bounder, chunky, requestedDepth, 1, requestedDepth,
  444.                                 colors, {returns} bitsSize);
  445.         
  446.                             {make a private device}
  447.                             bitsDevice := GDHandle(NewHandleClear(SIZEOF(GDevice)));
  448.                             Out(MemError); {must have failed because of heap paucity}
  449.         
  450.                             inverseTable := ITabHandle(NewHandle(0)); {create the inverse table}
  451.                             Out(MemError);
  452.                             preferredResolution := GetMainDevice^^.gdResPref; {preferred resolution ??? is this cool?}
  453.         
  454.                             WITH bitsDevice^^ DO BEGIN {set up the other fields of the device}
  455.                                 gdType := clutType;
  456.                                 gdITable := inverseTable;
  457.                                 gdResPref := preferredResolution;
  458.                                 gdFlags := 2 ** noDriver;
  459.                                 gdPMap := portPixMap;
  460.                             END; {WITH}
  461.                             InitGDevice(0, -1, bitsDevice); {sets gdRect from pixmap now; in future may do more}
  462.                             MakeITable(colors, inverseTable, preferredResolution);
  463.                             Out(QDError); {MakeITable failed}
  464.                         END ELSE BEGIN {when there is no private device}
  465.                                 {get the max. area device and set up the port’s pixmap accordingly}
  466.                                 IF NOT InitGBufferScreen(portPixMap, requestedBounds, bitsDevice, {returns} bitsSize) THEN
  467.                                     Out(noDeviceIntersectErr); {no devices intersect bounds}
  468.                                     
  469.                                 oldSeed := portPixMap^^.pmTable^^.ctSeed; {used to detect color updates}
  470.                         END;
  471.                         
  472.                     map := BitMapPtr(portPixMap^); {point to the map so we can set the portRect & visRgn}
  473.  
  474. {!!! ??? Do we need to call ForeColor and BackColor on the CGrafPort to set it up right?}
  475.  
  476.                     END {WITH}
  477.                 ELSE BEGIN {Classic QuickDraw}
  478.                     OpenPort(GrafPtr(bitsPort));
  479.                     WITH GrafPtr(bitsPort)^, portBits DO BEGIN
  480.                         bounds := bounder;
  481.                         {figure out how much space we need for our bits}
  482.                         WITH bounds DO BEGIN
  483.                             rowBytes := ((right + 15) DIV 16) * 2;
  484.                             {calculate an even # of words}
  485.                             bitsSize := bottom * LONGINT(rowBytes); {calculate the size}
  486.                         END; {WITH}
  487.                         map := @portBits; {point to the map so we can set it up}
  488.                     END; {WITH}
  489.                 END;
  490.                     
  491.                 WITH bitsPort^ DO BEGIN
  492.                     portRect := map^.bounds; {port's portRect is same as bounds}
  493.                     RectRgn(visRgn, portRect); {the visRgn must be changed}
  494.                     invalRegion := NewRgn;
  495.                     Out(MemError);
  496.                     CopyRgn(visRgn, invalRegion);
  497.                 END; {WITH}
  498.                 
  499.                 bits := Handle(NewImprovedGBuffer(bitsSize, needs32Bits, buffNotNeeded));
  500.                 notOurs := bits <> NIL;
  501.                 IF NOT notOurs THEN BEGIN
  502.                     IF requestedPolite THEN
  503.                         contiguousSpace := MaxBlock {make sure we have room without purging}
  504.                     ELSE
  505.                         PurgeSpace(totalSpace, contiguousSpace); {make sure we have room with purging}
  506.                     IF bitsSize + kOffscreenReserve > contiguousSpace THEN
  507.                         {if there is not enough room for the bits & reserve block, error out}
  508.                         Out(memFullErr);
  509.                     bits := NewHandleClear(bitsSize); {get space for the bits}
  510.                     Out(MemError);
  511.                     IF requestedPolite THEN
  512.                         HPurge(bits); {keep bits purgeable if we are polite}
  513.                 END;
  514.                 
  515.             END; {WITH}
  516.             HUnlock(offscreenHandle);
  517.  
  518.             SetPort(oldPort);
  519.         END; {IF GetMap(offscreenHandle) = NIL}
  520.  
  521.     AllocateBits := noErr;
  522. END; {AllocateBits}
  523.  
  524.  
  525.  
  526. PROCEDURE DisposeOffscreen(offscreenHandle: Handle);
  527. {Get rid of everything, including offscreenHandle itself.}
  528.  
  529. BEGIN {DisposeOffscreen}
  530.     IF offscreenHandle <> NIL THEN BEGIN
  531.         DeallocateBits(offscreenHandle);
  532.         DisposHandle(Handle(PrivateHandle(offscreenHandle)^^.requestedColors));
  533.         DisposHandle(offscreenHandle);
  534.     END; {IF offscreenHandle <> NIL}
  535. END; {DisposeOffscreen}
  536.  
  537.  
  538.  
  539. FUNCTION NewOffscreen(bounds: Rect; depth: INTEGER; colors: CTabHandle;
  540.                       memoryPolite: BOOLEAN; VAR buffNotNeeded: BOOLEAN; VAR offscreenHandle: Handle): OSErr;
  541. {Create an offscreenHandle. Return NIL if there is an error.}
  542.  
  543. VAR
  544.     aHandle    : Handle;
  545.  
  546.     PROCEDURE Out(error: OSErr);
  547.     BEGIN {Out}
  548.         IF error <> noErr THEN BEGIN
  549.             DisposHandle(aHandle); {dispose handle if we made it}
  550.             NewOffscreen := error;
  551.             offscreenHandle := NIL;
  552.             EXIT(NewOffscreen);
  553.         END; {IF error <> noErr}
  554.     END; {Out}
  555.  
  556. BEGIN {NewOffscreen}
  557.     aHandle := NewHandleClear(SizeOf(PrivateRecord)); {make the private block}
  558.     Out(MemError);
  559.  
  560.     MoveHHi(aHandle); {fly this high so it doesn't fragment the heap}
  561.     HLock(aHandle);
  562.     WITH PrivateHandle(aHandle)^^ DO BEGIN
  563.         requestedBounds := bounds;
  564.         IF theMac.hasColorQD THEN
  565.             requestedDepth := depth
  566.         ELSE
  567.             requestedDepth := noDepth;
  568.         IF requestedDepth > 0 THEN BEGIN {if we need a private device}
  569.             Out(HandToHand(Handle(colors)));
  570.             requestedColors := colors;
  571.         END;
  572.         requestedPolite := memoryPolite;
  573.     END; {WITH}
  574.     HUnlock(aHandle);
  575.  
  576.     {now, we have filled in all of the fields of offscreenHandle}
  577.     NewOffscreen := AllocateBits(aHandle, buffNotNeeded);
  578.     offscreenHandle := aHandle;
  579. END; {NewOffscreen}
  580.  
  581.  
  582.  
  583. FUNCTION CheckOffscreen(offscreenHandle: Handle; VAR drawNeeded: BOOLEAN): OSErr;
  584.  
  585. VAR
  586.     error            : OSErr;
  587.     newSeed            : LONGINT;
  588.     buffNotNeeded    : BOOLEAN;
  589.  
  590. BEGIN {CheckOffscreen}
  591.     drawNeeded := TRUE; {default we must draw if offscreen invalid}
  592.     error := noErr; {no error either}
  593.     IF offscreenHandle <> NIL THEN
  594.         WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  595.             IF requestedDepth = kMaxDepth THEN {if it's not a private device}
  596.                 IF oldSeed <> bitsDevice^^.gdPMap^^.pmTable^^.ctSeed THEN {we have a color update, Houston}
  597.                     DeallocateBits(offscreenHandle);
  598.  
  599.             drawNeeded := GetMap(offscreenHandle) = NIL;
  600.             error := AllocateBits(offscreenHandle, buffNotNeeded); {allocate if deallocated}
  601.         END; {WITH}
  602.             
  603.     IF GetMap(offscreenHandle) <> NIL THEN
  604.         drawNeeded := drawNeeded | (NOT EmptyRgn(PrivateHandle(offscreenHandle)^^.invalRegion));
  605.         {can’t use the width for invalRegion since we may have done an AllocateBits}
  606.         
  607.     CheckOffscreen := error;
  608. END; {CheckOffscreen}
  609.  
  610.  
  611. PROCEDURE ValidRectOffscreen(offscreenHandle: Handle; window: WindowPtr; validRect: Rect);
  612.  
  613. VAR
  614.     validRgn    : RgnHandle;
  615.  
  616. BEGIN
  617.     validRgn := NewRgn;
  618.     RectRgn(validRgn, validRect);
  619.     ValidRgnOffscreen(offscreenHandle, window, validRgn);
  620.     DisposeRgn(validRgn);
  621. END; {ValidRectOffscreen}
  622.  
  623.  
  624. PROCEDURE ValidRgnOffscreen(offscreenHandle: Handle; window: WindowPtr; validRegion: RgnHandle);
  625.  
  626. VAR
  627.     savedPort    : GrafPtr;
  628.     
  629. BEGIN
  630.     IF window <> NIL THEN BEGIN
  631.         GetPort(savedPort);
  632.         SetPort(window);
  633.         ValidRgn(validRegion);
  634.         SetPort(savedPort);
  635.     END;
  636.     IF GetMap(offscreenHandle) <> NIL THEN
  637.         WITH PrivateHandle(offscreenHandle)^^ DO
  638.             DiffRgn(invalRegion, validRegion, invalRegion);
  639. END; {ValidRgnOffscreen}
  640.  
  641.  
  642. PROCEDURE InvalRectOffscreen(offscreenHandle: Handle; window: WindowPtr; invalidRect: Rect);
  643.  
  644. VAR
  645.     invalidRgn    : RgnHandle;
  646.  
  647. BEGIN
  648.     invalidRgn := NewRgn;
  649.     RectRgn(invalidRgn, invalidRect);
  650.     InvalRgnOffscreen(offscreenHandle, window, invalidRgn);
  651.     DisposeRgn(invalidRgn);
  652. END; {InvalRectOffscreen}
  653.  
  654.  
  655. PROCEDURE InvalRgnOffscreen(offscreenHandle: Handle; window: WindowPtr; invalidRgn: RgnHandle);
  656.  
  657. VAR
  658.     savedPort    : GrafPtr;
  659.     
  660. BEGIN
  661.     IF window <> NIL THEN BEGIN
  662.         GetPort(savedPort);
  663.         SetPort(window);
  664.         InvalRgn(invalidRgn);
  665.         SetPort(savedPort);
  666.     END;
  667.     IF GetMap(offscreenHandle) <> NIL THEN
  668.         WITH PrivateHandle(offscreenHandle)^^ DO
  669.             UnionRgn(invalRegion, invalidRgn, invalRegion);
  670. END; {ValidRgnOffscreen}
  671.  
  672.  
  673. FUNCTION CheckBoundsOffscreen(offscreenHandle: Handle; newBounds: Rect;
  674.                        VAR drawNeeded: BOOLEAN): OSErr;
  675.  
  676. VAR
  677.     somethingChanged    : BOOLEAN;
  678.     rightDevice            : GDHandle;
  679.  
  680. BEGIN {CheckBoundsOffscreen}
  681.     IF offscreenHandle <> NIL THEN
  682.         WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  683.             {check to see if the size of the port changed}
  684.             WITH newBounds, bitsPort^.portRect.botRight DO
  685.                 somethingChanged := (right - left <> h) | (bottom - top <> v);
  686.  
  687.             IF NOT somethingChanged THEN BEGIN
  688.                 {check to see if the depth of the screen changed}
  689.                 rightDevice := NIL;
  690.                 IF requestedDepth = kMaxDepth THEN
  691.                     rightDevice := GetMaxDevice(newBounds);
  692.                 somethingChanged := (rightDevice <> NIL) & (rightDevice <> bitsDevice);
  693.             END;
  694.  
  695.             IF somethingChanged THEN BEGIN {we have a color update, Houston}
  696.                 DeallocateBits(offscreenHandle);
  697.                 requestedBounds := newBounds;
  698.             END;
  699.         END; {WITH}
  700.     CheckBoundsOffscreen := CheckOffscreen(offscreenHandle, drawNeeded);
  701. END; {CheckBoundsOffscreen}
  702.  
  703.  
  704.  
  705. PROCEDURE BeginOffscreenDrawing(offscreenHandle: Handle; port: GrafPtr);
  706. BEGIN {BeginOffscreenDrawing}
  707.     IF GetMap(offscreenHandle) <> NIL THEN
  708.     {note side effect here of setting up map for drawing (stuffing baseAddr)}
  709.         WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  710.             IF NOT notOurs THEN {if we, not the system, allocated the bits}
  711.                 {lock the bits down}
  712.                 HLock(bits);
  713.  
  714.             {we need the pixmap locked while we are using it}
  715.             IF theMac.hasColorQD THEN
  716.                 HLock(Handle(bitsPort^.portPixMap));
  717.  
  718.             drawingPort := port;
  719.             GetPort(savedPort);
  720.             
  721.             {set up the port and the device}
  722.             
  723.             IF drawingPort = NIL THEN
  724.                 SetPort(GrafPtr(bitsPort))
  725.             ELSE BEGIN
  726.                 SetPort(drawingPort);
  727.                 {save the visRgn and replace it with our private one}
  728.                 savedVisRgn := drawingPort^.visRgn;
  729.                 drawingPort^.visRgn := bitsPort^.visRgn;
  730.                 
  731.                 savedMap := drawingPort^.portBits;
  732.                 
  733.                 IF theMac.hasColorQD THEN
  734.                     SetPortPix(bitsPort^.portPixMap)
  735.                 ELSE
  736.                     SetPortBits(GrafPtr(bitsPort)^.portBits);
  737.             END; {for caller-supplied port case}
  738.             
  739.             IF theMac.hasColorQD THEN BEGIN
  740.                 savedDevice := GetGDevice;
  741.                 SetGDevice(bitsDevice);
  742.             END; {IF theMac.hasColorQD}
  743.             
  744.             WITH thePort^ DO
  745.                 {intersect the invalidated offscreen region with private visRgn}
  746.                 SectRgn(invalRegion, visRgn, visRgn);
  747.         END; {WITH}
  748. END; {BeginOffscreenDrawing}
  749.  
  750.  
  751.  
  752. PROCEDURE EndOffscreenDrawing(offscreenHandle: Handle);
  753.  
  754. TYPE
  755.     PMapHandlePtr    = ^PixMapHandle;
  756.     
  757. BEGIN {EndOffscreenDrawing}
  758.     IF GetMap(offscreenHandle) <> NIL THEN
  759.         WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  760.             {restore the map, device, port}
  761.             
  762.             IF drawingPort <> NIL THEN BEGIN
  763.                 SetPort(drawingPort);
  764.                 drawingPort^.visRgn := savedVisRgn; {restore original visRgn}
  765.                 
  766.                 IF theMac.hasColorQD THEN
  767.                     SetPortPix(PMapHandlePtr(@savedMap)^)
  768.                 ELSE
  769.                     SetPortBits(savedMap);
  770.             END; {for caller-supplied port case}                
  771.                 
  772.             IF theMac.hasColorQD THEN
  773.                 SetGDevice(savedDevice);
  774.                 
  775.             SetPort(savedPort);
  776.  
  777.             {we only need the pixmap locked while we are using it}
  778.             IF theMac.hasColorQD THEN
  779.                 HUnlock(Handle(bitsPort^.portPixMap));
  780.  
  781.             IF NOT notOurs THEN
  782.                 {unlock the bits so they can float}
  783.                 HUnlock(bits);
  784.             
  785.             WITH bitsPort^ DO
  786.                 RectRgn(visRgn, portRect); {set private visRgn back to full size}
  787.         END; {WITH}
  788. END; {EndOffscreenDrawing}
  789.  
  790.  
  791. PROCEDURE BeginUpdateOffscreen(offscreenHandle: Handle; window: WindowPtr);
  792. BEGIN
  793.     BeginUpdate(window);
  794.     IF GetMap(offscreenHandle) <> NIL THEN BEGIN
  795.         BeginOffscreenDrawing(offscreenHandle, window); {’sects invalRegion with visRgn}
  796.         {validate the entire contents of the offscreen buffer}
  797.         ValidRectOffscreen(offscreenHandle, NIL, PrivateHandle(offscreenHandle)^^.bitsPort^.portRect);
  798.     END;
  799. END; {BeginUpdateOffscreen}
  800.  
  801.  
  802. PROCEDURE EndUpdateOffscreen(offscreenHandle: Handle; window: WindowPtr);
  803. BEGIN
  804.     IF GetMap(offscreenHandle) <> NIL THEN BEGIN
  805.         EndOffscreenDrawing(offscreenHandle);
  806.         
  807.         SetPort(window);
  808.         ForeColor(blackColor);
  809.         BackColor(whiteColor);
  810.         {now copy from the offscreen buffer to the window (clipped to window’s visRgn)}
  811.         WITH PrivateHandle(offscreenHandle)^^ DO
  812.             CopyBits(GrafPtr(bitsPort)^.portBits, window^.portBits, bitsPort^.portRect,
  813.                 window^.portRect, srcCopy, NIL);
  814.     END;
  815.     EndUpdate(window);
  816. END; {EndUpdateOffscreen}
  817.  
  818.  
  819. {Create an off-screen pixel map for a port.
  820.  It will always use kMaxDepth and always be memoryPolite.}
  821.  
  822. FUNCTION NewOffscreenForWindow(window: WindowPtr; VAR buffNotNeeded: BOOLEAN;
  823.                                VAR offscreenHandle: Handle): OSErr;
  824.  
  825. VAR
  826.     savedPort    : GrafPtr;
  827.     globalRect    : Rect;
  828.  
  829. BEGIN {NewOffscreenForWindow}
  830.     SetPort(window);
  831.  
  832.     globalRect := window^.portRect; {calculate a global rect for this window}
  833.     WITH globalRect DO BEGIN
  834.         LocalToGlobal(topLeft);
  835.         LocalToGlobal(botRight);
  836.     END;
  837.  
  838.     NewOffscreenForWindow := NewOffscreen(globalRect, kMaxDepth, NIL, TRUE,
  839.         buffNotNeeded, offscreenHandle);
  840. END; {NewOffscreenForWindow}
  841.  
  842.  
  843.  
  844. (* ??? looks like these aren’t needed…
  845. {Begin an update for the window specified, using the specified off-screen
  846.  handle for bits. This sets the port to the window! …and temporarily nukes
  847.  the visRgn! ??? @@@ !!!}
  848.  
  849. PROCEDURE BeginOffscreenUpdate(offscreenHandle: Handle; window: WindowPtr);
  850. BEGIN {BeginOffscreenUpdate}
  851.     BeginOffscreenDrawing(offscreenHandle); {???!!! 
  852.     SetPort(window); {override the offscreen port which was set by BOU}
  853.     BeginUpdate(window);
  854.     
  855. END; {BeginOffscreenUpdate}
  856.  
  857.  
  858.  
  859. {Finish drawing to an off-screen port. Also copy the bits from the
  860.  port to the screen.}
  861.  
  862. PROCEDURE EndOffscreenUpdate(offscreenHandle: Handle; window: WindowPtr);
  863.  
  864.  
  865.  
  866. {Call this when the rectangle of the off-screen map needs to change, perhaps
  867.  due to a window moving on the main screen. This will also check for any change
  868.  in depth.}
  869.  
  870. FUNCTION UpdateOffscreenForWindow(offscreenHandle: Handle; window: WindowPtr;
  871.                                   VAR drawNeeded: BOOLEAN): OSErr;
  872. *)
  873.  
  874.  
  875. END. {Offscreen}
  876.